//@author A0100189M-reused

// Code credits to: http://developer.nokia.com/community/wiki/Code_Example_for_SlidingStackedWidget_class_in_Qt

#include "SlidingStackedWidget.h"

SlidingStackedWidget::SlidingStackedWidget(QWidget *parent) : QStackedWidget(parent) {
	if (parent!=0) {
		m_mainwindow=parent;
	} else {
		m_mainwindow=this;
		qDebug()<<"ATTENTION: untested mainwindow case !";
	}
	// Now, initialize some private variables with default values
	m_vertical=false;
	//setVerticalMode(true);
	m_speed=500;
	m_animationtype = QEasingCurve::OutCubic;  //check out the QEasingCurve documentation for different styles
	m_now=0;
	m_next=0;
	m_wrap=false;
	m_pnow=QPoint(0,0);
	m_active=false;
}


SlidingStackedWidget::~SlidingStackedWidget() {
}

void SlidingStackedWidget::setVerticalMode(bool vertical) {
	m_vertical=vertical;
}

void SlidingStackedWidget::setSpeed(int speed) {
	m_speed = speed;
}

void SlidingStackedWidget::setAnimation(enum QEasingCurve::Type animationtype) {
	m_animationtype = animationtype;
}

void SlidingStackedWidget::setWrap(bool wrap) {
	m_wrap=wrap;
}

void SlidingStackedWidget::slideInNext() {
	int now=currentIndex();
	if (m_wrap||(now<count()-1))
		// count is inherit from QStackedWidget
			slideInIdx(now+1);
}


void SlidingStackedWidget::slideInPrev() {
	int now=currentIndex();
	if (m_wrap||(now>0))
		slideInIdx(now-1);
}

void SlidingStackedWidget::slideInIdx(int idx, enum t_direction direction) {
	//int idx, t_direction direction=AUTOMATIC
	if (idx>count()-1) {
		direction=m_vertical ? TOP2BOTTOM : RIGHT2LEFT;
		idx=(idx)%count();
	}
	else if (idx<0) {
		direction= m_vertical ? BOTTOM2TOP: LEFT2RIGHT;
		idx=(idx+count())%count();
	}
	slideInWgt(widget ( idx ),direction);
	//widget() is a function inherited from QStackedWidget
}


void SlidingStackedWidget::slideInWgt(QWidget * newwidget, enum t_direction direction) {

	if (m_active) {
		return; // at the moment, do not allow re-entrance before an animation is completed.
		//other possibility may be to finish the previous animation abrupt, or
		//to revert the previous animation with a counter animation, before going ahead
		//or to revert the previous animation abrupt
		//and all those only, if the newwidget is not the same as that of the previous running animation.
	}
	else m_active=true;

	enum t_direction directionhint;
	int now=currentIndex();		//currentIndex() is a function inherited from QStackedWidget
	int next=indexOf(newwidget);
	if (now==next) {
		m_active=false;
		return;
	}
	else if (now<next){
		directionhint=m_vertical ? TOP2BOTTOM : RIGHT2LEFT;
	}
	else {
		directionhint=m_vertical ? BOTTOM2TOP : LEFT2RIGHT;
	}
	if (direction == AUTOMATIC) {
		direction=directionhint;
	}
	//NOW....
	//calculate the shifts

	int offsetx=frameRect().width(); //inherited from mother
	int offsety=frameRect().height();//inherited from mother

	//the following is important, to ensure that the new widget
	//has correct geometry information when sliding in first time
	widget(next)->setGeometry ( 0,  0, offsetx, offsety );

	if (direction==BOTTOM2TOP)  {
		offsetx=0;
		offsety=-offsety;
	}
	else if (direction==TOP2BOTTOM) {
		offsetx=0;
		//offsety=offsety;
	}
	else if (direction==RIGHT2LEFT) {
		offsetx=-offsetx;
		offsety=0;
	}
	else if (direction==LEFT2RIGHT) {
		//offsetx=offsetx;
		offsety=0;
	}
	//re-position the next widget outside/aside of the display area
	QPoint pnext=widget(next)->pos();
	QPoint pnow=widget(now)->pos();
	m_pnow=pnow;

	widget(next)->move(pnext.x()-offsetx,pnext.y()-offsety);
	//make it visible/show
	widget(next)->show();
	widget(next)->raise();

	//animate both, the now and next widget to the side, using animation framework
	QPropertyAnimation *animnow = new QPropertyAnimation(widget(now), "pos");

	animnow->setDuration(m_speed);
	animnow->setEasingCurve(m_animationtype);
	animnow->setStartValue(QPoint(pnow.x(), pnow.y()));
	animnow->setEndValue(QPoint(offsetx+pnow.x(), offsety+pnow.y()));
	QPropertyAnimation *animnext = new QPropertyAnimation(widget(next), "pos");
	animnext->setDuration(m_speed);
	animnext->setEasingCurve(m_animationtype);
	animnext->setStartValue(QPoint(-offsetx+pnext.x(), offsety+pnext.y()));
	animnext->setEndValue(QPoint(pnext.x(), pnext.y()));

	QParallelAnimationGroup *animgroup = new QParallelAnimationGroup;

	animgroup->addAnimation(animnow);
	animgroup->addAnimation(animnext);

	QObject::connect(animgroup, SIGNAL(finished()),this,SLOT(animationDoneSlot()));
	m_next=next;
	m_now=now;
	m_active=true;
	animgroup->start();

	//note; the rest is done via a connect from the animation ready;
	//animation->finished() provides a signal when animation is done;
	//so we connect this to some post processing slot,
	//that we implement here below in animationDoneSlot.
}


void SlidingStackedWidget::animationDoneSlot(void) {
	//when ready, call the QStackedWidget slot setCurrentIndex(int)
	setCurrentIndex(m_next);  //this function is inherit from QStackedWidget
	//then hide the outshifted widget now, and  (may be done already implicitely by QStackedWidget)
	widget(m_now)->hide();
	//then set the position of the outshifted widget now back to its original
	widget(m_now)->move(m_pnow);
	//so that the application could also still call the QStackedWidget original functions/slots for changings
	//widget(m_now)->update();
	//setCurrentIndex(m_next);  //this function is inherit from QStackedWidget
	m_active=false;
	emit animationFinished();
}



/* REFERENCES

http://doc.trolltech.com/4.6/animation-overview.html#easing-curves
http://doc.trolltech.com/4.6/qpropertyanimation.html
http://doc.trolltech.com/4.6/qanimationgroup.html

*/